tracing_opentelemetry_fmt/
lib.rs1use 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}