tracing_opentelemetry_fmt/
lib.rs

1use std::any::TypeId;
2
3use opentelemetry::trace::{TraceContextExt, Tracer};
4use tracing::{
5    field::FieldSet,
6    metadata::LevelFilter,
7    span::{Attributes, Record},
8    subscriber::Interest,
9    Event, Id, Metadata, Span, Subscriber, Value,
10};
11use tracing_opentelemetry::{OpenTelemetryLayer, OpenTelemetrySpanExt, PreSampledTracer};
12use tracing_subscriber::{
13    fmt::{FormatEvent, FormatFields, Layer as FmtLayer, MakeWriter},
14    layer::{Context, Layered},
15    registry::LookupSpan,
16    Layer,
17};
18
19pub struct OpenTelemetryFmtLayerBuilder<S, T1, N2, E2, W2> {
20    opentelemetry_layer: OpenTelemetryLayer<S, T1>,
21    fmt_layer: FmtLayer<S, N2, E2, W2>,
22}
23
24impl<S, T1, N2, E2, W2> OpenTelemetryFmtLayerBuilder<S, T1, N2, E2, W2> {
25    pub fn new(
26        opentelemetry_layer: OpenTelemetryLayer<S, T1>,
27        fmt_layer: FmtLayer<S, N2, E2, W2>,
28    ) -> Self {
29        Self {
30            opentelemetry_layer,
31            fmt_layer,
32        }
33    }
34}
35
36impl<S, T1, N2, E2, W2> OpenTelemetryFmtLayerBuilder<S, T1, N2, E2, W2>
37where
38    S: Subscriber + for<'a> LookupSpan<'a>,
39    T1: Tracer + PreSampledTracer + 'static,
40    N2: for<'writer> FormatFields<'writer> + 'static,
41    E2: FormatEvent<S, N2> + 'static,
42    W2: for<'writer> MakeWriter<'writer> + 'static,
43{
44    pub fn build(
45        self,
46    ) -> Layered<OpenTelemetryFmtLayer<S, N2, E2, W2>, OpenTelemetryLayer<S, T1>, S> {
47        let Self {
48            opentelemetry_layer,
49            fmt_layer,
50        } = self;
51        let opentelemetry_fmt_layer = OpenTelemetryFmtLayer { fmt_layer };
52        opentelemetry_layer.and_then(opentelemetry_fmt_layer)
53    }
54}
55
56pub struct OpenTelemetryFmtLayer<S, N2, E2, W2> {
57    fmt_layer: FmtLayer<S, N2, E2, W2>,
58}
59
60impl<S, N2, E2, W2> Layer<S> for OpenTelemetryFmtLayer<S, N2, E2, W2>
61where
62    S: Subscriber + for<'a> LookupSpan<'a>,
63    N2: for<'writer> FormatFields<'writer> + 'static,
64    E2: FormatEvent<S, N2> + 'static,
65    W2: for<'writer> MakeWriter<'writer> + 'static,
66{
67    fn on_layer(&mut self, subscriber: &mut S) {
68        self.fmt_layer.on_layer(subscriber)
69    }
70
71    fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
72        self.fmt_layer.register_callsite(metadata)
73    }
74
75    fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool {
76        self.fmt_layer.enabled(metadata, ctx)
77    }
78
79    fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
80        self.fmt_layer.on_new_span(attrs, id, ctx)
81    }
82
83    fn max_level_hint(&self) -> Option<LevelFilter> {
84        self.fmt_layer.max_level_hint()
85    }
86
87    fn on_record(&self, span: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
88        self.fmt_layer.on_record(span, values, ctx)
89    }
90
91    fn on_follows_from(&self, span: &Id, follows: &Id, ctx: Context<'_, S>) {
92        self.fmt_layer.on_follows_from(span, follows, ctx)
93    }
94
95    fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool {
96        self.fmt_layer.event_enabled(event, ctx)
97    }
98
99    fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
100        self.fmt_layer.on_event(event, ctx)
101    }
102
103    fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
104        let span_context = Span::current().context();
105        let opentelemetry_span = span_context.span();
106        let ids = if opentelemetry_span.span_context().is_valid() {
107            Some((
108                opentelemetry_span.span_context().trace_id().to_string(),
109                opentelemetry_span.span_context().span_id().to_string(),
110            ))
111        } else {
112            None
113        };
114
115        self.fmt_layer.on_enter(id, ctx.clone());
116
117        if let Some(ids) = ids {
118            let field_set = FieldSet::new(
119                &["trace.id", "span.id"],
120                ctx.metadata(id)
121                    .expect("Metadata not found, this is a bug")
122                    .callsite(),
123            );
124            let mut it = field_set.iter();
125            let trace_field = it.next().expect("Trace field not found, this is a bug");
126            let span_field = it.next().expect("Trace field not found, this is a bug");
127            let values = [
128                (&trace_field, Some(&ids.0 as &dyn Value)),
129                (&span_field, Some(&ids.1 as &dyn Value)),
130            ];
131            let values = field_set.value_set(&values);
132            let record = Record::new(&values);
133            self.fmt_layer.on_record(id, &record, ctx.clone());
134        }
135    }
136
137    fn on_exit(&self, id: &Id, ctx: Context<'_, S>) {
138        self.fmt_layer.on_exit(id, ctx)
139    }
140
141    fn on_close(&self, id: Id, ctx: Context<'_, S>) {
142        self.fmt_layer.on_close(id, ctx)
143    }
144
145    fn on_id_change(&self, old: &Id, new: &Id, ctx: Context<'_, S>) {
146        self.fmt_layer.on_id_change(old, new, ctx)
147    }
148
149    unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
150        self.fmt_layer.downcast_raw(id)
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use std::io::Write;
157
158    use opentelemetry::sdk::export::trace::stdout;
159    use tracing_subscriber::{
160        fmt::{self, format::FmtSpan},
161        layer::SubscriberExt,
162        util::SubscriberInitExt,
163    };
164
165    use super::*;
166
167    #[derive(Debug)]
168    struct Drain;
169
170    impl Write for Drain {
171        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
172            Ok(buf.len())
173        }
174
175        fn flush(&mut self) -> std::io::Result<()> {
176            Ok(())
177        }
178    }
179
180    #[test]
181    fn it_works() {
182        let fmt_layer = fmt::layer()
183            .with_thread_ids(true)
184            .with_target(true)
185            .with_span_events(FmtSpan::FULL);
186        let tracer = stdout::new_pipeline().with_writer(Drain).install_simple();
187        let opentelemetry_layer = tracing_opentelemetry::layer()
188            .with_exception_field_propagation(true)
189            .with_threads(true)
190            .with_tracer(tracer);
191
192        let opentelemetry_fmt_layer =
193            OpenTelemetryFmtLayerBuilder::new(opentelemetry_layer, fmt_layer).build();
194        tracing_subscriber::registry()
195            .with(opentelemetry_fmt_layer)
196            .try_init()
197            .expect("It should be successful");
198
199        tracing::info_span!("span1").in_scope(|| {
200            tracing::info!("in span1");
201        });
202    }
203}