use tracing_subscriber::registry::{LookupSpan, SpanRef};
use tracing_core::subscriber::Subscriber as Collect;
use tracing_subscriber::layer::Context;
use tracing_core::span::{Id, Attributes, Record};
use tracing_core::{Event, Field};
use crate::{Layer, FlattenFmt, NestedFmt, fluent, worker};
use core::fmt;
macro_rules! get_span {
($ctx:ident[$id:ident]) => {
match $ctx.span($id) {
Some(span) => span,
None => return,
}
}
}
pub trait FieldFormatter: 'static {
#[inline(always)]
fn on_new_span<C: Collect + for<'a> LookupSpan<'a>>(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, C>) {
let span = get_span!(ctx[id]);
if span.extensions().get::<fluent::Map>().is_none() {
let mut record = fluent::Map::new();
attrs.record(&mut record);
span.extensions_mut().insert(record);
}
}
#[inline(always)]
fn on_record<C: Collect + for<'a> LookupSpan<'a>>(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, C>) {
let span = get_span!(ctx[id]);
let mut extensions = span.extensions_mut();
if let Some(record) = extensions.get_mut::<fluent::Map>() {
values.record(record);
}
}
fn on_event<'a, R: LookupSpan<'a>>(&self, record: &mut fluent::Record, event: &Event<'_>, current_span: Option<SpanRef<'a, R>>);
}
impl FieldFormatter for NestedFmt {
#[inline(always)]
fn on_event<'a, R: LookupSpan<'a>>(&self, event_record: &mut fluent::Record, event: &Event<'_>, current_span: Option<SpanRef<'a, R>>) {
use core::ops::DerefMut;
event.record(event_record.deref_mut());
if let Some(span) = current_span {
for span in span.scope() {
let extensions = span.extensions();
if let Some(record) = extensions.get::<fluent::Map>() {
event_record.insert(span.name().to_owned(), record.clone().into());
}
}
}
let mut metadata = fluent::Map::new();
if let Some(name) = event.metadata().file() {
metadata.insert("file".to_owned(), name.to_owned().into());
}
if let Some(line) = event.metadata().line() {
metadata.insert("line".to_owned(), line.into());
}
metadata.insert("module".to_owned(), event.metadata().target().to_owned().into());
metadata.insert("level".to_owned(), event.metadata().level().to_owned().into());
event_record.insert("metadata".to_owned(), metadata.into());
}
}
impl FieldFormatter for FlattenFmt {
#[inline(always)]
fn on_event<'a, R: LookupSpan<'a>>(&self, event_record: &mut fluent::Record, event: &Event<'_>, current_span: Option<SpanRef<'a, R>>) {
use core::ops::DerefMut;
event.record(event_record.deref_mut());
if let Some(span) = current_span {
for span in span.scope() {
let extensions = span.extensions();
if let Some(record) = extensions.get::<fluent::Map>() {
event_record.update(record);
}
}
}
if let Some(name) = event.metadata().file() {
event_record.insert("file".to_owned(), name.to_owned().into());
}
if let Some(line) = event.metadata().line() {
event_record.insert("line".to_owned(), line.into());
}
event_record.insert("module".to_owned(), event.metadata().target().to_owned().into());
event_record.insert("level".to_owned(), event.metadata().level().to_owned().into());
}
}
impl tracing_core::field::Visit for fluent::Map {
#[inline(always)]
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
let value = format!("{:?}", value);
self.insert(field.name().to_owned(), value.into());
}
#[inline(always)]
fn record_i64(&mut self, field: &Field, value: i64) {
self.insert(field.name().to_owned(), value.into());
}
#[inline(always)]
fn record_u64(&mut self, field: &Field, value: u64) {
self.insert(field.name().to_owned(), value.into());
}
#[inline(always)]
fn record_bool(&mut self, field: &Field, value: bool) {
self.insert(field.name().to_owned(), value.into());
}
#[inline(always)]
fn record_str(&mut self, field: &Field, value: &str) {
self.insert(field.name().to_owned(), value.to_owned().into());
}
#[inline(always)]
fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
let value = format!("{}", value);
self.insert(field.name().to_owned(), value.into());
}
}
impl<F: FieldFormatter, W: worker::Consumer, C: Collect + for<'a> LookupSpan<'a>> tracing_subscriber::layer::Layer<C> for Layer<F, W> {
#[inline(always)]
fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, C>) {
self.fmt.on_new_span(attrs, id, ctx);
}
#[inline(always)]
fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, C>) {
self.fmt.on_record(id, values, ctx);
}
#[inline(always)]
fn on_enter(&self, _id: &Id, _ctx: Context<'_, C>) {
}
#[inline(always)]
fn on_exit(&self, _id: &Id, _ctx: Context<'_, C>) {
}
#[inline(always)]
fn on_close(&self, _id: Id, _ctx: Context<'_, C>) {
}
#[inline]
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, C>) {
let mut record = fluent::Record::now();
self.fmt.on_event(&mut record, event, ctx.event_span(event));
self.consumer.record(record);
}
}