1
- use std:: time:: SystemTime ;
1
+ use std:: { any :: TypeId , time:: SystemTime } ;
2
2
3
3
use opentelemetry:: {
4
4
KeyValue ,
@@ -9,11 +9,35 @@ use tracing::{Subscriber, field::Visit};
9
9
use tracing_opentelemetry:: { OpenTelemetrySpanExt , OtelData , PreSampledTracer } ;
10
10
use tracing_subscriber:: { Layer , registry:: LookupSpan } ;
11
11
12
- use crate :: { LogfireTracer , try_with_logfire_tracer } ;
12
+ use crate :: LogfireTracer ;
13
13
14
- pub ( crate ) struct LogfireTracingLayer ( pub ( crate ) opentelemetry_sdk:: trace:: Tracer ) ;
14
+ /// A `tracing` layer that bridges `tracing` spans to OpenTelemetry spans using the Logfire tracer.
15
+ ///
16
+ /// This layer is a wrapper around `tracing_opentelemetry::OpenTelemetryLayer` that adds additional
17
+ /// Logfire-specific metadata.
18
+ ///
19
+ /// See [`ShutdownHandler::tracing_layer`][crate::ShutdownHandler::tracing_layer] for how to use
20
+ /// this layer.
21
+ pub struct LogfireTracingLayer < S > {
22
+ tracer : LogfireTracer ,
23
+ otel_layer : tracing_opentelemetry:: OpenTelemetryLayer < S , opentelemetry_sdk:: trace:: Tracer > ,
24
+ }
15
25
16
- impl < S > Layer < S > for LogfireTracingLayer
26
+ impl < S > LogfireTracingLayer < S >
27
+ where
28
+ S : Subscriber + for < ' span > LookupSpan < ' span > ,
29
+ {
30
+ /// Create a new `LogfireTracingLayer` with the given tracer.
31
+ pub ( crate ) fn new ( tracer : LogfireTracer ) -> Self {
32
+ let otel_layer = tracing_opentelemetry:: layer ( )
33
+ . with_error_records_to_exceptions ( true )
34
+ . with_tracer ( tracer. inner . clone ( ) ) ;
35
+
36
+ LogfireTracingLayer { tracer, otel_layer }
37
+ }
38
+ }
39
+
40
+ impl < S > Layer < S > for LogfireTracingLayer < S >
17
41
where
18
42
S : Subscriber + for < ' span > LookupSpan < ' span > ,
19
43
{
23
47
id : & tracing:: span:: Id ,
24
48
ctx : tracing_subscriber:: layer:: Context < ' _ , S > ,
25
49
) {
50
+ // Delegate to OpenTelemetry layer first
51
+ self . otel_layer . on_new_span ( attrs, id, ctx. clone ( ) ) ;
52
+
53
+ // Add Logfire-specific attributes
26
54
let span = ctx. span ( id) . expect ( "span not found" ) ;
27
55
let mut extensions = span. extensions_mut ( ) ;
28
56
if let Some ( otel_data) = extensions. get_mut :: < OtelData > ( ) {
46
74
///
47
75
/// e.g. <https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/blob/5830c9113b0d42b72167567bf8e5f4c6b20933c8/axum-tracing-opentelemetry/src/middleware/trace_extractor.rs#L132>
48
76
fn on_enter ( & self , id : & tracing:: span:: Id , ctx : tracing_subscriber:: layer:: Context < ' _ , S > ) {
77
+ // Delegate to OpenTelemetry layer first
78
+ self . otel_layer . on_enter ( id, ctx. clone ( ) ) ;
79
+
49
80
let span = ctx. span ( id) . expect ( "span not found" ) ;
50
81
let mut extensions = span. extensions_mut ( ) ;
51
82
58
89
// Guaranteed to be on first entering of the span
59
90
if let Some ( otel_data) = extensions. get_mut :: < OtelData > ( ) {
60
91
// Emit a pending span, if this span will be sampled.
61
- let context = self . 0 . sampled_context ( otel_data) ;
92
+ let context = self . tracer . inner . sampled_context ( otel_data) ;
62
93
let sampling_result = otel_data
63
94
. builder
64
95
. sampling_result
@@ -110,14 +141,15 @@ where
110
141
) ) ;
111
142
}
112
143
113
- pending_span_builder. span_id = Some ( self . 0 . new_span_id ( ) ) ;
144
+ pending_span_builder. span_id = Some ( self . tracer . inner . new_span_id ( ) ) ;
114
145
115
146
let start_time = pending_span_builder
116
147
. start_time
117
148
. expect ( "otel SDK sets start time" ) ;
118
149
119
150
// emit pending span
120
- let mut pending_span = pending_span_builder. start_with_context ( & self . 0 , & context) ;
151
+ let mut pending_span =
152
+ pending_span_builder. start_with_context ( & self . tracer . inner , & context) ;
121
153
pending_span. end_with_timestamp ( start_time) ;
122
154
}
123
155
}
@@ -131,39 +163,59 @@ where
131
163
event : & tracing:: Event < ' _ > ,
132
164
_ctx : tracing_subscriber:: layer:: Context < ' _ , S > ,
133
165
) {
134
- try_with_logfire_tracer ( |tracer| {
135
- // All events are emitted as log spans
136
- emit_event_as_log_span ( tracer, event, & tracing:: Span :: current ( ) ) ;
137
- } ) ;
166
+ // Don't delegate events to OpenTelemetry layer, we emit them as log spans instead.
167
+ // FIXME: can we get current span from `ctx`?
168
+ emit_event_as_log_span ( & self . tracer , event, & tracing:: Span :: current ( ) ) ;
138
169
}
139
- }
140
170
141
- /// Helper to print spans when dropped; if it was never entered then the pending span
142
- /// is never sent (the console writer uses pending spans).
143
- ///
144
- /// This needs to be a separate layer so that it can access the `OtelData` before the
145
- /// `tracing_opentelemetry` layer removes it.
146
- pub struct LogfireTracingPendingSpanNotSentLayer ;
171
+ fn on_exit ( & self , id : & tracing:: span:: Id , ctx : tracing_subscriber:: layer:: Context < ' _ , S > ) {
172
+ self . otel_layer . on_exit ( id, ctx) ;
173
+ }
147
174
148
- impl < S > Layer < S > for LogfireTracingPendingSpanNotSentLayer
149
- where
150
- S : Subscriber + for < ' span > LookupSpan < ' span > ,
151
- {
152
175
fn on_close ( & self , id : tracing:: span:: Id , ctx : tracing_subscriber:: layer:: Context < ' _ , S > ) {
153
176
let span = ctx. span ( & id) . expect ( "span not found" ) ;
154
- let mut extensions = span. extensions_mut ( ) ;
155
-
156
- if extensions. get_mut :: < LogfirePendingSpanSent > ( ) . is_some ( ) {
157
- return ;
158
- }
177
+ let extensions = span. extensions ( ) ;
159
178
160
- // Guaranteed to be on first entering of the span
161
- if let Some ( otel_data) = extensions. get_mut :: < OtelData > ( ) {
162
- try_with_logfire_tracer ( |tracer| {
163
- if let Some ( writer) = & tracer. console_writer {
179
+ // We write pending spans to the console; if the pending span was never created then
180
+ // we have to manually write it now.
181
+ if extensions. get :: < LogfirePendingSpanSent > ( ) . is_none ( ) {
182
+ if let Some ( otel_data) = extensions. get :: < OtelData > ( ) {
183
+ if let Some ( writer) = & self . tracer . console_writer {
164
184
writer. write_tracing_opentelemetry_data ( otel_data) ;
165
185
}
166
- } ) ;
186
+ }
187
+ }
188
+
189
+ // Delegate to OpenTelemetry layer after handling pending span (it will remove the
190
+ // `OtelData` so cannot do before).
191
+ drop ( extensions) ;
192
+ drop ( span) ;
193
+ self . otel_layer . on_close ( id, ctx) ;
194
+ }
195
+
196
+ fn on_follows_from (
197
+ & self ,
198
+ span : & tracing:: span:: Id ,
199
+ follows : & tracing:: span:: Id ,
200
+ ctx : tracing_subscriber:: layer:: Context < ' _ , S > ,
201
+ ) {
202
+ self . otel_layer . on_follows_from ( span, follows, ctx) ;
203
+ }
204
+
205
+ fn on_record (
206
+ & self ,
207
+ span : & tracing:: span:: Id ,
208
+ values : & tracing:: span:: Record < ' _ > ,
209
+ ctx : tracing_subscriber:: layer:: Context < ' _ , S > ,
210
+ ) {
211
+ self . otel_layer . on_record ( span, values, ctx) ;
212
+ }
213
+
214
+ unsafe fn downcast_raw ( & self , id : TypeId ) -> Option < * const ( ) > {
215
+ if id == TypeId :: of :: < Self > ( ) {
216
+ Some ( std:: ptr:: from_ref ( self ) . cast ( ) )
217
+ } else {
218
+ unsafe { self . otel_layer . downcast_raw ( id) }
167
219
}
168
220
}
169
221
}
0 commit comments