Skip to content

Commit bad0a1b

Browse files
authored
feat(appender-tracing): re-gate span attribute enrichment behind experimental_span_attributes feature (#3505)
1 parent f744509 commit bad0a1b

4 files changed

Lines changed: 50 additions & 11 deletions

File tree

opentelemetry-appender-tracing/CHANGELOG.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22

33
## vNext
44

5-
- **Add tracing span attribute enrichment.** When enabled, attributes attached
6-
to active [`tracing`] spans are copied onto each emitted log record. "Span"
7-
here refers to a [`tracing::span!`][tracing-span] from the [`tracing`] crate
8-
(the appender's source), **not** an OpenTelemetry span.
5+
- **Add tracing span attribute enrichment (experimental).** When enabled,
6+
attributes attached to active [`tracing`] spans are copied onto each emitted
7+
log record. "Span" here refers to a [`tracing::span!`][tracing-span] from the
8+
[`tracing`] crate (the appender's source), **not** an OpenTelemetry span.
9+
10+
Gated behind the new **`experimental_span_attributes`** cargo feature. As
11+
with all `experimental_*` features in this repo, the API may change without
12+
a major version bump until it is stabilized; once stable, the feature flag
13+
will be removed.
914

1015
Enrichment is **disabled by default** (no per-span overhead) and must be
1116
opted into at runtime via a single builder method that accepts a

opentelemetry-appender-tracing/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pprof = { workspace = true }
3434
[features]
3535
default = []
3636
experimental_metadata_attributes = ["dep:tracing-log"]
37+
experimental_span_attributes = []
3738
bench_profiling = []
3839

3940
[[bench]]
@@ -47,6 +48,7 @@ harness = false
4748
[[bench]]
4849
name = "span-attributes"
4950
harness = false
51+
required-features = ["experimental_span_attributes"]
5052

5153
[lib]
5254
bench = false

opentelemetry-appender-tracing/src/layer.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use opentelemetry::{
22
logs::{AnyValue, LogRecord, Logger, LoggerProvider, Severity},
33
Key,
44
};
5+
#[cfg(feature = "experimental_span_attributes")]
56
use std::borrow::Cow;
7+
#[cfg(feature = "experimental_span_attributes")]
68
use std::collections::HashSet;
79
use tracing_core::Level;
810
#[cfg(feature = "experimental_metadata_attributes")]
@@ -183,11 +185,13 @@ impl<LR: LogRecord> tracing::field::Visit for EventVisitor<'_, LR> {
183185
/// Visitor to extract fields from a tracing span.
184186
/// Takes a mutable reference to a Vec to append fields to, and an optional
185187
/// allowlist to include only named attributes.
188+
#[cfg(feature = "experimental_span_attributes")]
186189
struct SpanFieldVisitor<'a> {
187190
attributes: &'a mut Vec<(Key, AnyValue)>,
188191
allowlist: Option<&'a HashSet<Cow<'static, str>>>,
189192
}
190193

194+
#[cfg(feature = "experimental_span_attributes")]
191195
impl SpanFieldVisitor<'_> {
192196
#[inline]
193197
fn allowed(&self, field: &tracing::field::Field) -> bool {
@@ -196,6 +200,7 @@ impl SpanFieldVisitor<'_> {
196200
}
197201
}
198202

203+
#[cfg(feature = "experimental_span_attributes")]
199204
impl tracing::field::Visit for SpanFieldVisitor<'_> {
200205
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
201206
if self.allowed(field) {
@@ -295,6 +300,7 @@ impl tracing::field::Visit for SpanFieldVisitor<'_> {
295300
/// Similar to how `tracing_subscriber::fmt::FormattedFields` stores formatted
296301
/// field data directly in span extensions - the Registry's internal locking
297302
/// provides the necessary synchronization.
303+
#[cfg(feature = "experimental_span_attributes")]
298304
#[derive(Debug)]
299305
struct StoredSpanAttributes {
300306
attributes: Vec<(Key, AnyValue)>,
@@ -311,13 +317,16 @@ struct StoredSpanAttributes {
311317
///
312318
/// [`tracing`]: https://crates.io/crates/tracing
313319
/// [`tracing::span!`]: https://docs.rs/tracing/latest/tracing/macro.span.html
320+
#[cfg(feature = "experimental_span_attributes")]
314321
pub struct TracingSpanAttributes(TracingSpanAttributesInner);
315322

323+
#[cfg(feature = "experimental_span_attributes")]
316324
enum TracingSpanAttributesInner {
317325
All,
318326
Allowlist(HashSet<Cow<'static, str>>),
319327
}
320328

329+
#[cfg(feature = "experimental_span_attributes")]
321330
impl TracingSpanAttributes {
322331
/// Copy **all** tracing-span attributes onto log records.
323332
pub fn all() -> Self {
@@ -333,6 +342,7 @@ impl TracingSpanAttributes {
333342
}
334343
}
335344

345+
#[cfg(feature = "experimental_span_attributes")]
336346
impl std::fmt::Debug for TracingSpanAttributes {
337347
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
338348
match &self.0 {
@@ -356,6 +366,7 @@ where
356366
// - `None` => disabled (default). No per-span work, no scope walk.
357367
// - `Some(All)` => copy all tracing-span attributes onto log records.
358368
// - `Some(Allowlist(set))` => copy only attributes whose keys are in `set`.
369+
#[cfg(feature = "experimental_span_attributes")]
359370
span_attributes: Option<TracingSpanAttributesInner>,
360371
}
361372

@@ -377,6 +388,7 @@ where
377388
// See https://github.com/open-telemetry/semantic-conventions/issues/1550
378389
logger: provider.logger(""),
379390
_phantom: Default::default(),
391+
#[cfg(feature = "experimental_span_attributes")]
380392
span_attributes: None,
381393
}
382394
}
@@ -389,6 +401,7 @@ where
389401
{
390402
logger: L,
391403
_phantom: std::marker::PhantomData<P>,
404+
#[cfg(feature = "experimental_span_attributes")]
392405
span_attributes: Option<TracingSpanAttributesInner>,
393406
}
394407

@@ -413,6 +426,7 @@ where
413426
///
414427
/// [`tracing`]: https://crates.io/crates/tracing
415428
/// [`tracing::span!`]: https://docs.rs/tracing/latest/tracing/macro.span.html
429+
#[cfg(feature = "experimental_span_attributes")]
416430
pub fn with_tracing_span_attributes(mut self, span_attributes: TracingSpanAttributes) -> Self {
417431
self.span_attributes = Some(span_attributes.0);
418432
self
@@ -422,6 +436,7 @@ where
422436
OpenTelemetryTracingBridge {
423437
logger: self.logger,
424438
_phantom: self._phantom,
439+
#[cfg(feature = "experimental_span_attributes")]
425440
span_attributes: self.span_attributes,
426441
}
427442
}
@@ -433,7 +448,12 @@ where
433448
P: LoggerProvider<Logger = L> + Send + Sync + 'static,
434449
L: Logger + Send + Sync + 'static,
435450
{
436-
fn on_event(&self, event: &tracing::Event<'_>, ctx: tracing_subscriber::layer::Context<'_, S>) {
451+
fn on_event(
452+
&self,
453+
event: &tracing::Event<'_>,
454+
#[cfg_attr(not(feature = "experimental_span_attributes"), allow(unused_variables))]
455+
ctx: tracing_subscriber::layer::Context<'_, S>,
456+
) {
437457
let metadata = event.metadata();
438458
let severity = severity_of_level(metadata.level());
439459
let target = metadata.target();
@@ -457,6 +477,7 @@ where
457477
log_record.set_severity_text(metadata.level().as_str());
458478

459479
// Extract tracing-span attributes if enrichment is enabled.
480+
#[cfg(feature = "experimental_span_attributes")]
460481
if self.span_attributes.is_some() {
461482
// Collect attributes from all parent spans (root to leaf), including current span
462483
if let Some(scope) = ctx.event_scope(event) {
@@ -482,6 +503,7 @@ where
482503
self.logger.emit(log_record);
483504
}
484505

506+
#[cfg(feature = "experimental_span_attributes")]
485507
fn on_new_span(
486508
&self,
487509
attrs: &tracing::span::Attributes<'_>,
@@ -515,6 +537,7 @@ where
515537
}
516538
}
517539

540+
#[cfg(feature = "experimental_span_attributes")]
518541
fn on_record(
519542
&self,
520543
id: &tracing::span::Id,
@@ -568,6 +591,7 @@ const fn severity_of_level(level: &Level) -> Severity {
568591
#[cfg(test)]
569592
mod tests {
570593
use crate::layer;
594+
#[cfg(feature = "experimental_span_attributes")]
571595
use crate::layer::TracingSpanAttributes;
572596
use opentelemetry::logs::Severity;
573597
use opentelemetry::trace::TracerProvider;
@@ -1176,6 +1200,7 @@ mod tests {
11761200
}
11771201

11781202
#[test]
1203+
#[cfg(feature = "experimental_span_attributes")]
11791204
fn tracing_appender_span_context_enrichment_enabled() {
11801205
// Arrange
11811206
let exporter = InMemoryLogExporter::default();
@@ -1227,6 +1252,7 @@ mod tests {
12271252
}
12281253

12291254
#[test]
1255+
#[cfg(feature = "experimental_span_attributes")]
12301256
fn tracing_appender_nested_spans_collect_all_parent_attributes() {
12311257
// Arrange
12321258
let exporter = InMemoryLogExporter::default();
@@ -1288,6 +1314,7 @@ mod tests {
12881314
}
12891315

12901316
#[test]
1317+
#[cfg(feature = "experimental_span_attributes")]
12911318
fn tracing_appender_span_context_with_various_types() {
12921319
// Arrange
12931320
let exporter = InMemoryLogExporter::default();
@@ -1397,6 +1424,7 @@ mod tests {
13971424
}
13981425

13991426
#[test]
1427+
#[cfg(feature = "experimental_span_attributes")]
14001428
fn tracing_appender_span_record_after_creation() {
14011429
// This test verifies that span fields recorded AFTER span creation
14021430
// are captured via the on_record implementation.
@@ -1465,6 +1493,7 @@ mod tests {
14651493
}
14661494

14671495
#[test]
1496+
#[cfg(feature = "experimental_span_attributes")]
14681497
fn tracing_appender_span_attribute_allowlist_includes_only_named() {
14691498
let exporter = InMemoryLogExporter::default();
14701499
let provider = SdkLoggerProvider::builder()
@@ -1501,6 +1530,7 @@ mod tests {
15011530
}
15021531

15031532
#[test]
1533+
#[cfg(feature = "experimental_span_attributes")]
15041534
fn tracing_appender_span_attribute_allowlist_nested_spans() {
15051535
let exporter = InMemoryLogExporter::default();
15061536
let provider = SdkLoggerProvider::builder()
@@ -1545,6 +1575,7 @@ mod tests {
15451575
}
15461576

15471577
#[test]
1578+
#[cfg(feature = "experimental_span_attributes")]
15481579
fn tracing_appender_span_attributes_all_copies_all() {
15491580
// `TracingSpanAttributes::all()` enables enrichment and copies all
15501581
// tracing-span attributes (no filtering).
@@ -1584,6 +1615,7 @@ mod tests {
15841615
}
15851616

15861617
#[test]
1618+
#[cfg(feature = "experimental_span_attributes")]
15871619
fn tracing_appender_span_attribute_allowlist_with_on_record() {
15881620
let exporter = InMemoryLogExporter::default();
15891621
let provider = SdkLoggerProvider::builder()
@@ -1625,6 +1657,7 @@ mod tests {
16251657
}
16261658

16271659
#[test]
1660+
#[cfg(feature = "experimental_span_attributes")]
16281661
fn tracing_appender_empty_allowlist_copies_nothing() {
16291662
// An empty allowlist means "allow nothing" — enrichment is enabled
16301663
// but no span attributes match, so none are copied.

opentelemetry-appender-tracing/src/lib.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,12 @@
130130
//! scopes can be copied onto each emitted log record. **"Span" here refers to a `tracing` span,
131131
//! not an `opentelemetry::trace::Span`.**
132132
//!
133+
//! Gated behind the `experimental_span_attributes` cargo feature.
134+
//!
133135
//! Enrichment is **disabled by default** (zero per-span overhead) and must be opted into
134136
//! via the builder:
135137
//!
136-
//! ```rust
137-
//! # use opentelemetry_sdk::logs::SdkLoggerProvider;
138-
//! # use opentelemetry_stdout::LogExporter;
139-
//! # let exporter = LogExporter::default();
140-
//! # let provider = SdkLoggerProvider::builder().with_simple_exporter(exporter).build();
138+
//! ```ignore
141139
//! use opentelemetry_appender_tracing::layer::{OpenTelemetryTracingBridge, TracingSpanAttributes};
142140
//!
143141
//! // Copy ALL tracing-span attributes onto log records:
@@ -161,9 +159,10 @@
161159
//!
162160
//! ## Feature Flags
163161
//!
162+
//! - `experimental_span_attributes`: Enables tracing-span attribute enrichment
163+
//! (`TracingSpanAttributes`, `with_tracing_span_attributes`).
164164
//! - `experimental_metadata_attributes`: Adds source code metadata (`code.filepath`,
165165
//! `code.filename`, `code.namespace`, `code.lineno`) as log record attributes.
166-
//! This is experimental and may change in future releases.
167166
//!
168167
//!
169168
//! ## Limitations

0 commit comments

Comments
 (0)