use std::marker::PhantomData;
use tracing::Subscriber;
use tracing_core::span;
use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer};
use crate::field_mapping::{
ExtractedFields, FieldExtractor, FieldMapping, TypedFieldSetter,
};
use crate::guard_stack;
use crate::span_info::{SpanInfo, SPAN_INFO_KEY};
use crate::FromFieldValue;
pub struct DcontextLayer<S> {
field_mappings: Vec<FieldMapping>,
include_span_info: bool,
_subscriber: PhantomData<fn(S)>,
}
impl<S> DcontextLayer<S> {
pub fn new() -> Self {
Self {
field_mappings: Vec::new(),
include_span_info: false,
_subscriber: PhantomData,
}
}
pub fn builder() -> DcontextLayerBuilder<S> {
DcontextLayerBuilder {
field_mappings: Vec::new(),
include_span_info: false,
_subscriber: PhantomData,
}
}
}
impl<S> Default for DcontextLayer<S> {
fn default() -> Self {
Self::new()
}
}
pub struct DcontextLayerBuilder<S> {
field_mappings: Vec<FieldMapping>,
include_span_info: bool,
_subscriber: PhantomData<fn(S)>,
}
impl<S> DcontextLayerBuilder<S> {
pub fn map_field<T: FromFieldValue>(mut self, field_name: &'static str) -> Self {
self.field_mappings.push(FieldMapping {
field_name,
context_key: field_name,
setter: Box::new(TypedFieldSetter::<T>::new()),
});
self
}
pub fn map_field_as<T: FromFieldValue>(
mut self,
field_name: &'static str,
context_key: &'static str,
) -> Self {
self.field_mappings.push(FieldMapping {
field_name,
context_key,
setter: Box::new(TypedFieldSetter::<T>::new()),
});
self
}
pub fn include_span_info(mut self) -> Self {
self.include_span_info = true;
self
}
pub fn build(self) -> DcontextLayer<S> {
DcontextLayer {
field_mappings: self.field_mappings,
include_span_info: self.include_span_info,
_subscriber: PhantomData,
}
}
}
impl<S> Layer<S> for DcontextLayer<S>
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
fn on_new_span(
&self,
attrs: &span::Attributes<'_>,
id: &span::Id,
ctx: Context<'_, S>,
) {
if self.field_mappings.is_empty() {
return;
}
let span = match ctx.span(id) {
Some(s) => s,
None => return,
};
let field_names: Vec<&'static str> =
self.field_mappings.iter().map(|m| m.field_name).collect();
let mut extractor = FieldExtractor::new(&field_names);
attrs.record(&mut extractor);
if !extractor.extracted.is_empty() {
let mut extensions = span.extensions_mut();
extensions.insert(extractor.extracted);
}
}
fn on_record(
&self,
id: &span::Id,
values: &span::Record<'_>,
ctx: Context<'_, S>,
) {
if self.field_mappings.is_empty() {
return;
}
let span = match ctx.span(id) {
Some(s) => s,
None => return,
};
let field_names: Vec<&'static str> =
self.field_mappings.iter().map(|m| m.field_name).collect();
let mut extractor = FieldExtractor::new(&field_names);
values.record(&mut extractor);
if !extractor.extracted.is_empty() {
let mut extensions = span.extensions_mut();
if let Some(existing) = extensions.get_mut::<ExtractedFields>() {
existing.string_values.extend(extractor.extracted.string_values);
existing.u64_values.extend(extractor.extracted.u64_values);
existing.i64_values.extend(extractor.extracted.i64_values);
existing.bool_values.extend(extractor.extracted.bool_values);
} else {
extensions.insert(extractor.extracted);
}
}
}
fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
dcontext::force_thread_local(|| {
let guard = dcontext::enter_scope();
if !self.field_mappings.is_empty() {
if let Some(span) = ctx.span(id) {
let extensions = span.extensions();
if let Some(fields) = extensions.get::<ExtractedFields>() {
for mapping in &self.field_mappings {
if let Some(v) = fields.string_values.get(mapping.field_name) {
mapping.setter.set_from_str(mapping.context_key, v);
} else if let Some(&v) = fields.u64_values.get(mapping.field_name) {
mapping.setter.set_from_u64(mapping.context_key, v);
} else if let Some(&v) = fields.i64_values.get(mapping.field_name) {
mapping.setter.set_from_i64(mapping.context_key, v);
} else if let Some(&v) = fields.bool_values.get(mapping.field_name) {
mapping.setter.set_from_bool(mapping.context_key, v);
}
}
}
}
}
if self.include_span_info {
if let Some(span) = ctx.span(id) {
let metadata = span.metadata();
let info = SpanInfo {
name: metadata.name().to_string(),
target: metadata.target().to_string(),
level: metadata.level().to_string(),
};
dcontext::set_context(SPAN_INFO_KEY, info);
}
}
guard_stack::push_guard(id, guard);
});
}
fn on_exit(&self, id: &span::Id, _ctx: Context<'_, S>) {
dcontext::force_thread_local(|| {
guard_stack::pop_guard(id);
});
}
fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
dcontext::force_thread_local(|| {
guard_stack::pop_guard(&id);
});
if let Some(span) = ctx.span(&id) {
let mut extensions = span.extensions_mut();
extensions.remove::<ExtractedFields>();
}
}
}