1
1
use crate :: layer:: WithContext ;
2
- use opentelemetry:: { trace:: SpanContext , trace:: Status , Context , Key , KeyValue , Value } ;
2
+ use opentelemetry:: {
3
+ time,
4
+ trace:: { SpanContext , Status } ,
5
+ Context , Key , KeyValue , Value ,
6
+ } ;
7
+ use std:: { borrow:: Cow , time:: SystemTime } ;
3
8
4
9
/// Utility functions to allow tracing [`Span`]s to accept and return
5
10
/// [OpenTelemetry] [`Context`]s.
@@ -152,20 +157,76 @@ pub trait OpenTelemetrySpanExt {
152
157
/// app_root.set_status(Status::Ok);
153
158
/// ```
154
159
fn set_status ( & self , status : Status ) ;
160
+
161
+ /// Adds an OpenTelemetry event directly to this span, bypassing `tracing::event!`.
162
+ /// This allows for adding events with dynamic attribute keys, similar to `set_attribute` for span attributes.
163
+ /// Events are added with the current timestamp.
164
+ ///
165
+ /// # Examples
166
+ ///
167
+ /// ```rust
168
+ /// use opentelemetry::{KeyValue};
169
+ /// use tracing_opentelemetry::OpenTelemetrySpanExt;
170
+ /// use tracing::Span;
171
+ ///
172
+ /// let app_root = tracing::span!(tracing::Level::INFO, "processing_request");
173
+ ///
174
+ /// let dynamic_attrs = vec![
175
+ /// KeyValue::new("job_id", "job-123"),
176
+ /// KeyValue::new("user.id", "user-xyz"),
177
+ /// ];
178
+ ///
179
+ /// // Add event using the extension method
180
+ /// app_root.add_event("job_started".to_string(), dynamic_attrs);
181
+ ///
182
+ /// // ... perform work ...
183
+ ///
184
+ /// app_root.add_event("job_completed", vec![KeyValue::new("status", "success")]);
185
+ /// ```
186
+ fn add_event ( & self , name : impl Into < Cow < ' static , str > > , attributes : Vec < KeyValue > ) ;
187
+
188
+ /// Adds an OpenTelemetry event with a specific timestamp directly to this span.
189
+ /// Similar to `add_event`, but allows overriding the event timestamp.
190
+ ///
191
+ /// # Examples
192
+ ///
193
+ /// ```rust
194
+ /// use opentelemetry::{KeyValue};
195
+ /// use tracing_opentelemetry::OpenTelemetrySpanExt;
196
+ /// use tracing::Span;
197
+ /// use std::time::{Duration, SystemTime};
198
+ /// use std::borrow::Cow;
199
+ ///
200
+ /// let app_root = tracing::span!(tracing::Level::INFO, "historical_event_processing");
201
+ ///
202
+ /// let event_time = SystemTime::now() - Duration::from_secs(60);
203
+ /// let event_attrs = vec![KeyValue::new("record_id", "rec-456")];
204
+ /// let event_name: Cow<'static, str> = "event_from_past".into();
205
+ ///
206
+ /// app_root.add_event_with_timestamp(event_name, event_time, event_attrs);
207
+ /// ```
208
+ fn add_event_with_timestamp (
209
+ & self ,
210
+ name : impl Into < Cow < ' static , str > > ,
211
+ timestamp : SystemTime ,
212
+ attributes : Vec < KeyValue > ,
213
+ ) ;
155
214
}
156
215
157
216
impl OpenTelemetrySpanExt for tracing:: Span {
158
217
fn set_parent ( & self , cx : Context ) {
159
218
let mut cx = Some ( cx) ;
160
219
self . with_subscriber ( move |( id, subscriber) | {
161
- if let Some ( get_context) = subscriber. downcast_ref :: < WithContext > ( ) {
162
- get_context. with_context ( subscriber, id, move |data, _tracer| {
163
- if let Some ( cx) = cx. take ( ) {
164
- data. parent_cx = cx;
165
- data. builder . sampling_result = None ;
166
- }
167
- } ) ;
168
- }
220
+ let Some ( get_context) = subscriber. downcast_ref :: < WithContext > ( ) else {
221
+ return ;
222
+ } ;
223
+ get_context. with_context ( subscriber, id, move |data, _tracer| {
224
+ let Some ( cx) = cx. take ( ) else {
225
+ return ;
226
+ } ;
227
+ data. parent_cx = cx;
228
+ data. builder . sampling_result = None ;
229
+ } ) ;
169
230
} ) ;
170
231
}
171
232
@@ -178,63 +239,94 @@ impl OpenTelemetrySpanExt for tracing::Span {
178
239
let mut cx = Some ( cx) ;
179
240
let mut att = Some ( attributes) ;
180
241
self . with_subscriber ( move |( id, subscriber) | {
181
- if let Some ( get_context) = subscriber. downcast_ref :: < WithContext > ( ) {
182
- get_context. with_context ( subscriber, id, move |data, _tracer| {
183
- if let Some ( cx) = cx. take ( ) {
184
- let attr = att. take ( ) . unwrap_or_default ( ) ;
185
- let follows_link = opentelemetry:: trace:: Link :: new ( cx, attr, 0 ) ;
186
- data. builder
187
- . links
188
- . get_or_insert_with ( || Vec :: with_capacity ( 1 ) )
189
- . push ( follows_link) ;
190
- }
191
- } ) ;
192
- }
242
+ let Some ( get_context) = subscriber. downcast_ref :: < WithContext > ( ) else {
243
+ return ;
244
+ } ;
245
+ get_context. with_context ( subscriber, id, move |data, _tracer| {
246
+ let Some ( cx) = cx. take ( ) else {
247
+ return ;
248
+ } ;
249
+ let attr = att. take ( ) . unwrap_or_default ( ) ;
250
+ let follows_link = opentelemetry:: trace:: Link :: new ( cx, attr, 0 ) ;
251
+ data. builder
252
+ . links
253
+ . get_or_insert_with ( || Vec :: with_capacity ( 1 ) )
254
+ . push ( follows_link) ;
255
+ } ) ;
193
256
} ) ;
194
257
}
195
258
}
196
259
197
260
fn context ( & self ) -> Context {
198
261
let mut cx = None ;
199
262
self . with_subscriber ( |( id, subscriber) | {
200
- if let Some ( get_context) = subscriber. downcast_ref :: < WithContext > ( ) {
201
- get_context. with_context ( subscriber, id, |builder, tracer| {
202
- cx = Some ( tracer. sampled_context ( builder) ) ;
203
- } )
204
- }
263
+ let Some ( get_context) = subscriber. downcast_ref :: < WithContext > ( ) else {
264
+ return ;
265
+ } ;
266
+ get_context. with_context ( subscriber, id, |builder, tracer| {
267
+ cx = Some ( tracer. sampled_context ( builder) ) ;
268
+ } )
205
269
} ) ;
206
270
207
271
cx. unwrap_or_default ( )
208
272
}
209
273
210
274
fn set_attribute ( & self , key : impl Into < Key > , value : impl Into < Value > ) {
211
275
self . with_subscriber ( move |( id, subscriber) | {
212
- if let Some ( get_context) = subscriber. downcast_ref :: < WithContext > ( ) {
213
- let mut key = Some ( key. into ( ) ) ;
214
- let mut value = Some ( value. into ( ) ) ;
215
- get_context. with_context ( subscriber, id, move |builder, _| {
216
- if builder. builder . attributes . is_none ( ) {
217
- builder. builder . attributes = Some ( Default :: default ( ) ) ;
218
- }
219
- builder
220
- . builder
221
- . attributes
222
- . as_mut ( )
223
- . unwrap ( )
224
- . push ( KeyValue :: new ( key. take ( ) . unwrap ( ) , value. take ( ) . unwrap ( ) ) ) ;
225
- } )
226
- }
276
+ let Some ( get_context) = subscriber. downcast_ref :: < WithContext > ( ) else {
277
+ return ;
278
+ } ;
279
+ let mut key = Some ( key. into ( ) ) ;
280
+ let mut value = Some ( value. into ( ) ) ;
281
+ get_context. with_context ( subscriber, id, move |builder, _| {
282
+ if builder. builder . attributes . is_none ( ) {
283
+ builder. builder . attributes = Some ( Default :: default ( ) ) ;
284
+ }
285
+ builder
286
+ . builder
287
+ . attributes
288
+ . as_mut ( )
289
+ . unwrap ( )
290
+ . push ( KeyValue :: new ( key. take ( ) . unwrap ( ) , value. take ( ) . unwrap ( ) ) ) ;
291
+ } )
227
292
} ) ;
228
293
}
229
294
230
295
fn set_status ( & self , status : Status ) {
231
296
self . with_subscriber ( move |( id, subscriber) | {
232
297
let mut status = Some ( status) ;
233
- if let Some ( get_context) = subscriber. downcast_ref :: < WithContext > ( ) {
234
- get_context. with_context ( subscriber, id, move |builder, _| {
235
- builder. builder . status = status. take ( ) . unwrap ( ) ;
236
- } ) ;
237
- }
298
+ let Some ( get_context) = subscriber. downcast_ref :: < WithContext > ( ) else {
299
+ return ;
300
+ } ;
301
+ get_context. with_context ( subscriber, id, move |builder, _| {
302
+ builder. builder . status = status. take ( ) . unwrap ( ) ;
303
+ } ) ;
304
+ } ) ;
305
+ }
306
+
307
+ fn add_event ( & self , name : impl Into < Cow < ' static , str > > , attributes : Vec < KeyValue > ) {
308
+ self . add_event_with_timestamp ( name, time:: now ( ) , attributes) ;
309
+ }
310
+
311
+ fn add_event_with_timestamp (
312
+ & self ,
313
+ name : impl Into < Cow < ' static , str > > ,
314
+ timestamp : SystemTime ,
315
+ attributes : Vec < KeyValue > ,
316
+ ) {
317
+ self . with_subscriber ( move |( id, subscriber) | {
318
+ let mut event = Some ( opentelemetry:: trace:: Event :: new (
319
+ name, timestamp, attributes, 0 ,
320
+ ) ) ;
321
+ let Some ( get_context) = subscriber. downcast_ref :: < WithContext > ( ) else {
322
+ return ;
323
+ } ;
324
+ get_context. with_context ( subscriber, id, move |data, _tracer| {
325
+ let Some ( event) = event. take ( ) else {
326
+ return ;
327
+ } ;
328
+ data. builder . events . get_or_insert_with ( Vec :: new) . push ( event) ;
329
+ } ) ;
238
330
} ) ;
239
331
}
240
332
}
0 commit comments