Skip to content

Commit 887be87

Browse files
authored
Merge branch 'main' into Robo210-patch-1
2 parents 871b15f + 581ae61 commit 887be87

File tree

9 files changed

+290
-52
lines changed

9 files changed

+290
-52
lines changed

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ crate-type = ["rlib"]
1414

1515
[features]
1616
global_filter = []
17+
# Enable OpenTelemetry trace context extraction. When enabled, span_id and trace_id
18+
# will be extracted from tracing-opentelemetry's OtelData span extensions when available.
19+
opentelemetry = ["dep:tracing-opentelemetry", "dep:opentelemetry"]
1720

1821
[dependencies]
1922
tracing = {version = "0.1", default-features = false}
@@ -25,6 +28,10 @@ thiserror = {version="2", default-features = false}
2528
hashers = "1"
2629
hashbrown = "0.15"
2730

31+
# Optional OpenTelemetry dependencies
32+
tracing-opentelemetry = {version = "0.32", optional = true}
33+
opentelemetry = {version = "0.31", optional = true, default-features = false, features = ["trace"]}
34+
2835
[target.'cfg(not(target_os = "linux"))'.dependencies]
2936
tracelogging = ">= 1.2.0"
3037
tracelogging_dynamic = ">= 1.2.0"

benches/etw.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,7 @@ pub fn etw_benchmark(c: &mut Criterion) {
127127
name: "evtname",
128128
Level::INFO,
129129
1,
130-
field1 = 1,
131-
field2 = "asdf",
132-
field3 = 1.1,
130+
{ field1 = 1, field2 = "asdf", field3 = 1.1 },
133131
"Enabled event!"
134132
);
135133
})

src/layer/layer_impl.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,20 @@ where
121121
.event_span(event)
122122
.map_or(0, |evt| evt.parent().map_or(0, |p| p.id().into_u64()));
123123

124+
// Extract OpenTelemetry context if available
125+
#[cfg(feature = "opentelemetry")]
126+
let otel_context = ctx.event_span(event).and_then(|span| {
127+
let otel_ctx = crate::otel::extract_otel_context(&span);
128+
if otel_ctx.is_valid {
129+
Some((otel_ctx.trace_id, otel_ctx.span_id))
130+
} else {
131+
None
132+
}
133+
});
134+
135+
#[cfg(not(feature = "opentelemetry"))]
136+
let otel_context: Option<([u8; 32], [u8; 16])> = None;
137+
124138
let etw_meta = get_event_metadata(&event.metadata().callsite());
125139
let (name, keyword, tag) = if let Some(meta) = etw_meta {
126140
(event.metadata().name(), meta.kw, meta.event_tag)
@@ -137,6 +151,7 @@ where
137151
keyword,
138152
tag,
139153
event,
154+
otel_context,
140155
);
141156
}
142157

src/lib.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,11 @@
108108
//! to consumers asynchronously per the platform design.
109109
//!
110110
//! ### Heap Allocations
111-
//!
111+
//!
112112
//! The goal of this crate is to have no heap allocations in the hot path (when an event is logged).
113113
//! Currently there are a few circumstances when logging an event requires a heap
114114
//! allocation, outlined below.
115-
//!
115+
//!
116116
//! Total memory usage by this crate will vary based on workload, but will usually
117117
//! stay in the 10s or low 100s of kilobytes, and should remain stable once the
118118
//! first event has been logged.
@@ -122,14 +122,14 @@
122122
//!
123123
//! - Logging events with the [Debug][std::fmt::Debug] format specifier (`:?`) will
124124
//! necessitate a heap allocation to format the value into a string.
125-
//!
125+
//!
126126
//! <div class="warning">
127-
//!
127+
//!
128128
//! All event messages (the format strings to the event) are, for reasons
129129
//! outside of the control of this crate, formatted as debug formats rather than as
130130
//! a string. This effectively means every single event is forced to perform at least
131131
//! one heap allocation. In benchmarking, this does not seem to be a major performance issue.
132-
//!
132+
//!
133133
//! </div>
134134
//!
135135
//! - Logging strings copies them to the heap first. This is a side-effect of how
@@ -195,6 +195,11 @@ mod values;
195195
pub mod _details;
196196
pub mod error;
197197

198+
// OpenTelemetry integration module - only available with the "opentelemetry" feature
199+
#[cfg(feature = "opentelemetry")]
200+
#[cfg_attr(docsrs, doc(cfg(feature = "opentelemetry")))]
201+
pub(crate) mod otel;
202+
198203
pub use layer_builder::LayerBuilder;
199204

200205
mod layer;
@@ -361,6 +366,17 @@ macro_rules! etw_event {
361366
(name: $name:expr, $lvl:expr, $kw:expr, $($k:ident).+) => (
362367
$crate::etw_event!(name: $name, $lvl, $kw, 0, $($k).+,)
363368
);
369+
// Handle bare message string: etw_event!(name: "Name", Level::INFO, 1, "message")
370+
(name: $name:expr, $lvl:expr, $kw:expr, $msg:literal) => (
371+
$crate::etw_event!(
372+
target: module_path!(),
373+
name: $name,
374+
$lvl,
375+
$kw,
376+
0,
377+
{ message = $msg }
378+
)
379+
);
364380
(name: $name:expr, $lvl:expr, $kw:expr, $($arg:tt)+ ) => (
365381
$crate::etw_event!(target: module_path!(), name: $name, $lvl, $kw, 0, { $($arg)+ })
366382
);

src/native/etw.rs

Lines changed: 70 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ use crate::{
55
values::{event_values::*, *},
66
};
77
use chrono::{Datelike, Timelike};
8-
use std::io::{Cursor, Write};
98
use std::marker::PhantomData;
10-
use std::mem::MaybeUninit;
119
use std::{cell::RefCell, ops::DerefMut, pin::Pin, sync::Arc, time::SystemTime};
1210
use tracelogging::*;
1311
use tracelogging_dynamic::EventBuilder;
@@ -299,6 +297,7 @@ impl<Mode: OutputMode> super::EventWriter<NormalOutput> for Provider<Mode> {
299297
keyword: u64,
300298
event_tag: u32,
301299
event: &tracing::Event<'_>,
300+
_otel_context: Option<([u8; 32], [u8; 16])>,
302301
) {
303302
let mut activity_id: [u8; 16] = *GLOBAL_ACTIVITY_SEED;
304303
activity_id[0] = if current_span != 0 {
@@ -414,11 +413,33 @@ impl<Mode: OutputMode> super::EventWriter<CommonSchemaOutput> for Provider<Mode>
414413
{
415414
let span_name = span.name();
416415

417-
let span_id = unsafe {
418-
let mut span_id = MaybeUninit::<[u8; 16]>::uninit();
419-
let mut cur = Cursor::new((*span_id.as_mut_ptr()).as_mut_slice());
420-
write!(&mut cur, "{:16x}", span.id().into_u64()).expect("!write");
421-
span_id.assume_init()
416+
// Extract trace_id and span_id - prefer OpenTelemetry context when available
417+
#[cfg(feature = "opentelemetry")]
418+
let (trace_id, span_id) = {
419+
let otel_ctx = crate::otel::extract_otel_context(span);
420+
if otel_ctx.is_valid {
421+
(otel_ctx.trace_id, otel_ctx.span_id)
422+
} else {
423+
// Fall back to local tracing span ID
424+
let mut span_id_buf = [0u8; 16];
425+
let trace_id_buf = [0u8; 32];
426+
let _ = std::io::Write::write_fmt(
427+
&mut span_id_buf.as_mut_slice(),
428+
format_args!("{:016x}", span.id().into_u64()),
429+
);
430+
(trace_id_buf, span_id_buf)
431+
}
432+
};
433+
434+
#[cfg(not(feature = "opentelemetry"))]
435+
let (trace_id, span_id) = {
436+
let mut span_id_buf = [0u8; 16];
437+
let trace_id_buf = [0u8; 32];
438+
let _ = std::io::Write::write_fmt(
439+
&mut span_id_buf.as_mut_slice(),
440+
format_args!("{:016x}", span.id().into_u64()),
441+
);
442+
(trace_id_buf, span_id_buf)
422443
};
423444

424445
EBW.with(|eb| {
@@ -441,7 +462,7 @@ impl<Mode: OutputMode> super::EventWriter<CommonSchemaOutput> for Provider<Mode>
441462

442463
eb.add_struct("ext_dt", 2, 0);
443464
{
444-
eb.add_str8("traceId", "", OutType::Utf8, 0); // TODO
465+
eb.add_str8("traceId", trace_id, OutType::Utf8, 0);
445466
eb.add_str8("spanId", span_id, OutType::Utf8, 0);
446467
}
447468
}
@@ -465,11 +486,30 @@ impl<Mode: OutputMode> super::EventWriter<CommonSchemaOutput> for Provider<Mode>
465486
eb.add_str8("_typeName", "Span", OutType::Utf8, 0);
466487

467488
if let Some(parent) = span_parent {
468-
let parent_span_id = unsafe {
469-
let mut span_id = MaybeUninit::<[u8; 16]>::uninit();
470-
let mut cur = Cursor::new((*span_id.as_mut_ptr()).as_mut_slice());
471-
write!(&mut cur, "{:16x}", parent.id().into_u64()).expect("!write");
472-
span_id.assume_init()
489+
// Extract parent span_id - prefer OpenTelemetry context when available
490+
#[cfg(feature = "opentelemetry")]
491+
let parent_span_id = {
492+
let otel_ctx = crate::otel::extract_otel_context(&parent);
493+
if otel_ctx.is_valid {
494+
otel_ctx.span_id
495+
} else {
496+
let mut buf = [0u8; 16];
497+
let _ = std::io::Write::write_fmt(
498+
&mut buf.as_mut_slice(),
499+
format_args!("{:016x}", parent.id().into_u64()),
500+
);
501+
buf
502+
}
503+
};
504+
505+
#[cfg(not(feature = "opentelemetry"))]
506+
let parent_span_id = {
507+
let mut buf = [0u8; 16];
508+
let _ = std::io::Write::write_fmt(
509+
&mut buf.as_mut_slice(),
510+
format_args!("{:016x}", parent.id().into_u64()),
511+
);
512+
buf
473513
};
474514

475515
eb.add_str8("parentId", parent_span_id, OutType::Utf8, 0);
@@ -518,6 +558,7 @@ impl<Mode: OutputMode> super::EventWriter<CommonSchemaOutput> for Provider<Mode>
518558
keyword: u64,
519559
event_tag: u32,
520560
event: &tracing::Event<'_>,
561+
otel_context: Option<([u8; 32], [u8; 16])>,
521562
) {
522563
EBW.with(|eb| {
523564
let mut eb = eb.borrow_mut();
@@ -532,27 +573,33 @@ impl<Mode: OutputMode> super::EventWriter<CommonSchemaOutput> for Provider<Mode>
532573
eb.add_u16("__csver__", 0x0401, OutType::Signed, 0);
533574
eb.add_struct(
534575
"PartA",
535-
1 + if current_span != 0 { 1 } else { 0 }, /* + exts.len() as u8*/
576+
1 + if current_span != 0 || otel_context.is_some() { 1 } else { 0 }, /* + exts.len() as u8*/
536577
0,
537578
);
538579
{
539580
let time: String =
540581
chrono::DateTime::to_rfc3339(&chrono::DateTime::<chrono::Utc>::from(timestamp));
541582
eb.add_str8("time", time, OutType::Utf8, 0);
542583

543-
if current_span != 0 {
584+
// Use OTel context if available, otherwise fall back to local span ID
585+
if let Some((trace_id, span_id)) = otel_context {
544586
eb.add_struct("ext_dt", 2, 0);
545587
{
546-
let span_id = unsafe {
547-
let mut span_id = MaybeUninit::<[u8; 16]>::uninit();
548-
let mut cur = Cursor::new((*span_id.as_mut_ptr()).as_mut_slice());
549-
write!(&mut cur, "{:16x}", current_span).expect("!write");
550-
span_id.assume_init()
551-
};
552-
553-
eb.add_str8("traceId", "", OutType::Utf8, 0); // TODO
588+
eb.add_str8("traceId", trace_id, OutType::Utf8, 0);
554589
eb.add_str8("spanId", span_id, OutType::Utf8, 0);
555590
}
591+
} else if current_span != 0 {
592+
eb.add_struct("ext_dt", 2, 0);
593+
{
594+
let mut span_id_buf = [0u8; 16];
595+
let _ = std::io::Write::write_fmt(
596+
&mut span_id_buf.as_mut_slice(),
597+
format_args!("{:016x}", current_span),
598+
);
599+
600+
eb.add_str8("traceId", [0u8; 32], OutType::Utf8, 0);
601+
eb.add_str8("spanId", span_id_buf, OutType::Utf8, 0);
602+
}
556603
}
557604
}
558605

src/native/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,5 +170,6 @@ pub trait EventWriter<OutMode: OutputMode> {
170170
keyword: u64,
171171
event_tag: u32,
172172
event: &tracing::Event<'_>,
173+
otel_context: Option<([u8; 32], [u8; 16])>, // (trace_id, span_id) when opentelemetry feature is enabled
173174
);
174175
}

src/native/noop.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ impl<OutMode: OutputMode> crate::native::EventWriter<OutMode> for Provider<OutMo
8686
_keyword: u64,
8787
_event_tag: u32,
8888
_event: &tracing::Event<'_>,
89+
_otel_context: Option<([u8; 32], [u8; 16])>,
8990
) {
9091
}
9192
}

0 commit comments

Comments
 (0)