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};
use crate::guard_stack;
use crate::span_info::{SpanInfo, SPAN_INFO_KEY};
use crate::tracing_field::get_tracing_fields;
pub struct DcontextLayer<S> {
include_span_info: bool,
_subscriber: PhantomData<fn(S)>,
}
impl<S> DcontextLayer<S> {
pub fn new() -> Self {
Self {
include_span_info: false,
_subscriber: PhantomData,
}
}
pub fn builder() -> DcontextLayerBuilder<S> {
DcontextLayerBuilder {
include_span_info: false,
_subscriber: PhantomData,
}
}
}
impl<S> Default for DcontextLayer<S> {
fn default() -> Self {
Self::new()
}
}
pub struct DcontextLayerBuilder<S> {
include_span_info: bool,
_subscriber: PhantomData<fn(S)>,
}
impl<S> DcontextLayerBuilder<S> {
pub fn include_span_info(mut self) -> Self {
self.include_span_info = true;
self
}
pub fn build(self) -> DcontextLayer<S> {
DcontextLayer {
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>,
) {
let metadata_fields = get_tracing_fields();
let extract_names: Vec<&'static str> = metadata_fields
.iter()
.filter(|e| e.extract.is_some())
.map(|e| e.span_field)
.collect();
if extract_names.is_empty() {
return;
}
let span = match ctx.span(id) {
Some(s) => s,
None => return,
};
let mut extractor = FieldExtractor::new(&extract_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>,
) {
let metadata_fields = get_tracing_fields();
let extract_names: Vec<&'static str> = metadata_fields
.iter()
.filter(|e| e.extract.is_some())
.map(|e| e.span_field)
.collect();
if extract_names.is_empty() {
return;
}
let span = match ctx.span(id) {
Some(s) => s,
None => return,
};
let mut extractor = FieldExtractor::new(&extract_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 = if let Some(span) = ctx.span(id) {
let name = span.metadata().name();
dcontext::enter_named_scope(name)
} else {
dcontext::enter_scope()
};
let metadata_fields = get_tracing_fields();
if metadata_fields.iter().any(|e| e.extract.is_some()) {
if let Some(span) = ctx.span(id) {
let extensions = span.extensions();
if let Some(fields) = extensions.get::<ExtractedFields>() {
for entry in metadata_fields {
if let Some(ref extract) = entry.extract {
if let Some(v) = fields.string_values.get(entry.span_field) {
if let Some(ref f) = extract.from_str {
f(v, entry.context_key);
}
} else if let Some(&v) = fields.u64_values.get(entry.span_field) {
if let Some(ref f) = extract.from_u64 {
f(v, entry.context_key);
}
} else if let Some(&v) = fields.i64_values.get(entry.span_field) {
if let Some(ref f) = extract.from_i64 {
f(v, entry.context_key);
}
} else if let Some(&v) = fields.bool_values.get(entry.span_field) {
if let Some(ref f) = extract.from_bool {
f(v, entry.context_key);
}
}
}
}
}
}
}
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>();
}
}
}