1use tracing_subscriber::registry::{LookupSpan, SpanRef};
2use tracing_core::subscriber::Subscriber as Collect;
3use tracing_subscriber::layer::Context;
4use tracing_core::span::{Id, Attributes, Record};
5use tracing_core::{Event, Field};
6
7use crate::{Layer, FlattenFmt, NestedFmt, fluent, worker};
8
9use core::fmt;
10
11macro_rules! get_span {
12 ($ctx:ident[$id:ident]) => {
13 match $ctx.span($id) {
14 Some(span) => span,
15 None => return,
16 }
17 }
18}
19
20pub trait FieldFormatter: 'static {
22 #[inline(always)]
23 fn on_new_span<C: Collect + for<'a> LookupSpan<'a>>(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, C>) {
27 let span = get_span!(ctx[id]);
28
29 if span.extensions().get::<fluent::Map>().is_none() {
30 let mut record = fluent::Map::new();
31 attrs.record(&mut record);
32
33 span.extensions_mut().insert(record);
34 }
35 }
36
37 #[inline(always)]
38 fn on_record<C: Collect + for<'a> LookupSpan<'a>>(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, C>) {
43 let span = get_span!(ctx[id]);
44
45 let mut extensions = span.extensions_mut();
46 if let Some(record) = extensions.get_mut::<fluent::Map>() {
47 values.record(record);
48 }
49 }
50
51 fn on_event<'a, R: LookupSpan<'a>>(&self, record: &mut fluent::Record, event: &Event<'_>, current_span: Option<SpanRef<'a, R>>);
56}
57
58impl FieldFormatter for NestedFmt {
59 #[inline(always)]
60 fn on_event<'a, R: LookupSpan<'a>>(&self, event_record: &mut fluent::Record, event: &Event<'_>, current_span: Option<SpanRef<'a, R>>) {
61 use core::ops::DerefMut;
62
63 event.record(event_record.deref_mut());
64
65 if let Some(span) = current_span {
66 for span in span.scope() {
67 let extensions = span.extensions();
68 if let Some(record) = extensions.get::<fluent::Map>() {
69 event_record.insert(span.name().into(), record.clone().into());
70 }
71 }
72 }
73
74 let mut metadata = fluent::Map::new();
75
76 if let Some(name) = event.metadata().file() {
77 metadata.insert("file".into(), name.into());
78 }
79 if let Some(line) = event.metadata().line() {
80 metadata.insert("line".into(), line.into());
81 }
82 metadata.insert("module".into(), event.metadata().target().into());
83 metadata.insert("level".into(), event.metadata().level().to_owned().into());
84
85 event_record.insert("metadata".into(), metadata.into());
86 }
87}
88
89impl FieldFormatter for FlattenFmt {
90 #[inline(always)]
91 fn on_event<'a, R: LookupSpan<'a>>(&self, event_record: &mut fluent::Record, event: &Event<'_>, current_span: Option<SpanRef<'a, R>>) {
92 use core::ops::DerefMut;
93
94 event.record(event_record.deref_mut());
95
96 if let Some(span) = current_span {
97 for span in span.scope() {
98 let extensions = span.extensions();
99 if let Some(record) = extensions.get::<fluent::Map>() {
100 event_record.update(record);
101 }
102 }
103 }
104
105 if let Some(name) = event.metadata().file() {
106 event_record.insert("file".into(), name.into());
107 }
108 if let Some(line) = event.metadata().line() {
109 event_record.insert("line".into(), line.into());
110 }
111 event_record.insert("module".into(), event.metadata().target().into());
112 event_record.insert("level".into(), event.metadata().level().to_owned().into());
113 }
114}
115
116impl tracing_core::field::Visit for fluent::Map {
117 #[inline(always)]
118 fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
119 let value = format!("{:?}", value);
120 self.insert(field.name().into(), value.into());
121 }
122
123 #[inline(always)]
124 fn record_i64(&mut self, field: &Field, value: i64) {
125 self.insert(field.name().into(), value.into());
126 }
127
128 #[inline(always)]
129 fn record_u64(&mut self, field: &Field, value: u64) {
130 self.insert(field.name().into(), value.into());
131 }
132
133 #[inline(always)]
134 fn record_f64(&mut self, field: &Field, value: f64) {
135 self.insert(field.name().into(), value.into());
136 }
137
138 #[inline(always)]
139 fn record_bool(&mut self, field: &Field, value: bool) {
140 self.insert(field.name().into(), value.into());
141 }
142
143 #[inline(always)]
144 fn record_str(&mut self, field: &Field, value: &str) {
145 self.insert(field.name().into(), value.to_owned().into());
146 }
147
148 #[inline(always)]
149 fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
150 let value = format!("{}", value);
151 self.insert(field.name().into(), value.into());
152 }
153}
154
155impl<F: FieldFormatter, W: worker::Consumer, C: Collect + for<'a> LookupSpan<'a>> tracing_subscriber::layer::Layer<C> for Layer<F, W> {
156 #[inline(always)]
157 fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, C>) {
158 self.fmt.on_new_span(attrs, id, ctx);
159 }
160
161 #[inline(always)]
162 fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, C>) {
163 self.fmt.on_record(id, values, ctx);
164 }
165
166 #[inline(always)]
167 fn on_enter(&self, _id: &Id, _ctx: Context<'_, C>) {
168 }
169
170 #[inline(always)]
171 fn on_exit(&self, _id: &Id, _ctx: Context<'_, C>) {
172 }
173
174 #[inline(always)]
175 fn on_close(&self, _id: Id, _ctx: Context<'_, C>) {
176 }
177
178 #[inline]
179 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, C>) {
180 let mut record = fluent::Record::now();
181
182 self.fmt.on_event(&mut record, event, ctx.event_span(event));
183
184 self.consumer.record(record);
185 }
186}