use std::sync::{Arc, LazyLock};
use std::time::Instant;
use compact_str::CompactString;
use tracing::Metadata;
use tracing::callsite::{Callsite, DefaultCallsite, Identifier};
use tracing::field::FieldSet;
use tracing::metadata::Kind;
static EMPTY_CALLSITE: DefaultCallsite = {
static META: Metadata<'static> = Metadata::new(
"unfilled",
"tracing_cache::record",
tracing::Level::TRACE,
None,
None,
None,
FieldSet::new(&[], Identifier(&EMPTY_CALLSITE)),
Kind::EVENT,
);
DefaultCallsite::new(&META)
};
fn empty_metadata() -> &'static Metadata<'static> {
EMPTY_CALLSITE.metadata()
}
static UNFILLED_INSTANT: LazyLock<Instant> = LazyLock::new(Instant::now);
#[derive(Debug, Clone)]
pub enum FieldValue {
U64(u64),
I64(i64),
F64(f64),
Bool(bool),
Str(&'static str),
SmallString(CompactString),
SharedString(Arc<String>),
String(String),
}
impl FieldValue {
pub fn to_display_string(&self) -> CompactString {
use std::fmt::Write;
match self {
FieldValue::U64(v) => {
let mut s = CompactString::default();
let _ = write!(s, "{}", v);
s
}
FieldValue::I64(v) => {
let mut s = CompactString::default();
let _ = write!(s, "{}", v);
s
}
FieldValue::F64(v) => {
let mut s = CompactString::default();
let _ = write!(s, "{}", v);
s
}
FieldValue::Bool(v) => CompactString::const_new(if *v { "true" } else { "false" }),
FieldValue::Str(s) => CompactString::const_new(s),
FieldValue::SmallString(s) => s.clone(),
FieldValue::SharedString(s) => CompactString::from(s.as_str()),
FieldValue::String(s) => CompactString::from(s.as_str()),
}
}
pub fn contains(&self, needle: &str) -> bool {
match self {
FieldValue::Str(s) => s.contains(needle),
FieldValue::SmallString(s) => s.contains(needle),
FieldValue::SharedString(s) => s.contains(needle),
FieldValue::String(s) => s.contains(needle),
_ => self.to_display_string().contains(needle),
}
}
}
pub type FieldList = Vec<(&'static str, FieldValue)>;
#[inline]
pub fn field_get<'a>(fields: &'a FieldList, name: &str) -> Option<&'a FieldValue> {
fields.iter().find(|(k, _)| *k == name).map(|(_, v)| v)
}
#[derive(Clone, Debug, Default)]
pub struct EventRecord {
pub metadata: Option<&'static Metadata<'static>>,
pub fields: FieldList,
pub recorded_at: Option<Instant>,
}
impl EventRecord {
#[inline]
pub fn metadata(&self) -> &'static Metadata<'static> {
self.metadata.unwrap_or_else(empty_metadata)
}
#[inline]
pub fn recorded_at(&self) -> Instant {
self.recorded_at.unwrap_or(*UNFILLED_INSTANT)
}
pub fn field(&self, name: &str) -> Option<&FieldValue> {
field_get(&self.fields, name)
}
}
impl crate::object_pool::Resettable for EventRecord {
fn reset(&mut self) {
self.metadata = None;
self.fields.clear();
self.recorded_at = None;
}
}
#[derive(Clone, Debug)]
pub struct SpanRecord {
pub id: u64,
pub parent_id: Option<u64>,
pub metadata: &'static Metadata<'static>,
pub fields: FieldList,
pub events: Vec<crate::object_pool::ReuseRef<EventRecord>>,
pub opened_at: Instant,
pub closed_at: Option<Instant>,
}
impl SpanRecord {
pub fn field(&self, name: &str) -> Option<&FieldValue> {
field_get(&self.fields, name)
}
}
pub(crate) struct FieldVisitor<'a> {
pub fields: &'a mut FieldList,
}
impl FieldVisitor<'_> {
#[inline]
fn set(&mut self, name: &'static str, value: FieldValue) {
match self.fields.iter_mut().find(|(k, _)| *k == name) {
Some(slot) => slot.1 = value,
None => self.fields.push((name, value)),
}
}
}
impl tracing::field::Visit for FieldVisitor<'_> {
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
use std::fmt::Write;
let mut s = CompactString::default();
let _ = write!(s, "{:?}", value);
self.set(field.name(), FieldValue::SmallString(s));
}
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
self.set(
field.name(),
FieldValue::SmallString(CompactString::from(value)),
);
}
fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
self.set(field.name(), FieldValue::I64(value));
}
fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
self.set(field.name(), FieldValue::U64(value));
}
fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
self.set(field.name(), FieldValue::Bool(value));
}
fn record_f64(&mut self, field: &tracing::field::Field, value: f64) {
self.set(field.name(), FieldValue::F64(value));
}
fn record_error(
&mut self,
field: &tracing::field::Field,
value: &(dyn std::error::Error + 'static),
) {
use std::fmt::Write;
let mut s = CompactString::default();
let _ = write!(s, "{}", value);
self.set(field.name(), FieldValue::SmallString(s));
}
}