Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions opentelemetry-appender-tracing/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@

## vNext

- **Add tracing span attribute enrichment.** When enabled, attributes attached
to active [`tracing`] spans are copied onto each emitted log record. "Span"
here refers to a [`tracing::span!`][tracing-span] from the [`tracing`] crate
(the appender's source), **not** an OpenTelemetry span.
- **Add tracing span attribute enrichment (experimental).** When enabled,
attributes attached to active [`tracing`] spans are copied onto each emitted
log record. "Span" here refers to a [`tracing::span!`][tracing-span] from the
[`tracing`] crate (the appender's source), **not** an OpenTelemetry span.

Gated behind the new **`experimental_span_attributes`** cargo feature. As
with all `experimental_*` features in this repo, the API may change without
a major version bump until it is stabilized; once stable, the feature flag
will be removed.

Enrichment is **disabled by default** (no per-span overhead) and must be
opted into at runtime via a single builder method that accepts a
Expand Down
2 changes: 2 additions & 0 deletions opentelemetry-appender-tracing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pprof = { workspace = true }
[features]
default = []
experimental_metadata_attributes = ["dep:tracing-log"]
experimental_span_attributes = []
bench_profiling = []

[[bench]]
Expand All @@ -47,6 +48,7 @@ harness = false
[[bench]]
name = "span-attributes"
harness = false
required-features = ["experimental_span_attributes"]

[lib]
bench = false
Expand Down
35 changes: 34 additions & 1 deletion opentelemetry-appender-tracing/src/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use opentelemetry::{
logs::{AnyValue, LogRecord, Logger, LoggerProvider, Severity},
Key,
};
#[cfg(feature = "experimental_span_attributes")]
use std::borrow::Cow;
#[cfg(feature = "experimental_span_attributes")]
use std::collections::HashSet;
use tracing_core::Level;
#[cfg(feature = "experimental_metadata_attributes")]
Expand Down Expand Up @@ -183,11 +185,13 @@ impl<LR: LogRecord> tracing::field::Visit for EventVisitor<'_, LR> {
/// Visitor to extract fields from a tracing span.
/// Takes a mutable reference to a Vec to append fields to, and an optional
/// allowlist to include only named attributes.
#[cfg(feature = "experimental_span_attributes")]
struct SpanFieldVisitor<'a> {
attributes: &'a mut Vec<(Key, AnyValue)>,
allowlist: Option<&'a HashSet<Cow<'static, str>>>,
}

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

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

#[cfg(feature = "experimental_span_attributes")]
enum TracingSpanAttributesInner {
All,
Allowlist(HashSet<Cow<'static, str>>),
}

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

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

Expand All @@ -377,6 +388,7 @@ where
// See https://github.com/open-telemetry/semantic-conventions/issues/1550
logger: provider.logger(""),
_phantom: Default::default(),
#[cfg(feature = "experimental_span_attributes")]
span_attributes: None,
}
}
Expand All @@ -389,6 +401,7 @@ where
{
logger: L,
_phantom: std::marker::PhantomData<P>,
#[cfg(feature = "experimental_span_attributes")]
span_attributes: Option<TracingSpanAttributesInner>,
}

Expand All @@ -413,6 +426,7 @@ where
///
/// [`tracing`]: https://crates.io/crates/tracing
/// [`tracing::span!`]: https://docs.rs/tracing/latest/tracing/macro.span.html
#[cfg(feature = "experimental_span_attributes")]
pub fn with_tracing_span_attributes(mut self, span_attributes: TracingSpanAttributes) -> Self {
self.span_attributes = Some(span_attributes.0);
self
Expand All @@ -422,6 +436,7 @@ where
OpenTelemetryTracingBridge {
logger: self.logger,
_phantom: self._phantom,
#[cfg(feature = "experimental_span_attributes")]
span_attributes: self.span_attributes,
}
}
Expand All @@ -433,7 +448,12 @@ where
P: LoggerProvider<Logger = L> + Send + Sync + 'static,
L: Logger + Send + Sync + 'static,
{
fn on_event(&self, event: &tracing::Event<'_>, ctx: tracing_subscriber::layer::Context<'_, S>) {
fn on_event(
&self,
event: &tracing::Event<'_>,
#[cfg_attr(not(feature = "experimental_span_attributes"), allow(unused_variables))]
ctx: tracing_subscriber::layer::Context<'_, S>,
) {
let metadata = event.metadata();
let severity = severity_of_level(metadata.level());
let target = metadata.target();
Expand All @@ -457,6 +477,7 @@ where
log_record.set_severity_text(metadata.level().as_str());

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

#[cfg(feature = "experimental_span_attributes")]
fn on_new_span(
&self,
attrs: &tracing::span::Attributes<'_>,
Expand Down Expand Up @@ -515,6 +537,7 @@ where
}
}

#[cfg(feature = "experimental_span_attributes")]
fn on_record(
&self,
id: &tracing::span::Id,
Expand Down Expand Up @@ -568,6 +591,7 @@ const fn severity_of_level(level: &Level) -> Severity {
#[cfg(test)]
mod tests {
use crate::layer;
#[cfg(feature = "experimental_span_attributes")]
use crate::layer::TracingSpanAttributes;
use opentelemetry::logs::Severity;
use opentelemetry::trace::TracerProvider;
Expand Down Expand Up @@ -1176,6 +1200,7 @@ mod tests {
}

#[test]
#[cfg(feature = "experimental_span_attributes")]
fn tracing_appender_span_context_enrichment_enabled() {
// Arrange
let exporter = InMemoryLogExporter::default();
Expand Down Expand Up @@ -1227,6 +1252,7 @@ mod tests {
}

#[test]
#[cfg(feature = "experimental_span_attributes")]
fn tracing_appender_nested_spans_collect_all_parent_attributes() {
// Arrange
let exporter = InMemoryLogExporter::default();
Expand Down Expand Up @@ -1288,6 +1314,7 @@ mod tests {
}

#[test]
#[cfg(feature = "experimental_span_attributes")]
fn tracing_appender_span_context_with_various_types() {
// Arrange
let exporter = InMemoryLogExporter::default();
Expand Down Expand Up @@ -1397,6 +1424,7 @@ mod tests {
}

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

#[test]
#[cfg(feature = "experimental_span_attributes")]
fn tracing_appender_span_attribute_allowlist_includes_only_named() {
let exporter = InMemoryLogExporter::default();
let provider = SdkLoggerProvider::builder()
Expand Down Expand Up @@ -1501,6 +1530,7 @@ mod tests {
}

#[test]
#[cfg(feature = "experimental_span_attributes")]
fn tracing_appender_span_attribute_allowlist_nested_spans() {
let exporter = InMemoryLogExporter::default();
let provider = SdkLoggerProvider::builder()
Expand Down Expand Up @@ -1545,6 +1575,7 @@ mod tests {
}

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

#[test]
#[cfg(feature = "experimental_span_attributes")]
fn tracing_appender_span_attribute_allowlist_with_on_record() {
let exporter = InMemoryLogExporter::default();
let provider = SdkLoggerProvider::builder()
Expand Down Expand Up @@ -1625,6 +1657,7 @@ mod tests {
}

#[test]
#[cfg(feature = "experimental_span_attributes")]
fn tracing_appender_empty_allowlist_copies_nothing() {
// An empty allowlist means "allow nothing" — enrichment is enabled
// but no span attributes match, so none are copied.
Expand Down
11 changes: 5 additions & 6 deletions opentelemetry-appender-tracing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,12 @@
//! scopes can be copied onto each emitted log record. **"Span" here refers to a `tracing` span,
//! not an `opentelemetry::trace::Span`.**
//!
//! Gated behind the `experimental_span_attributes` cargo feature.
//!
//! Enrichment is **disabled by default** (zero per-span overhead) and must be opted into
//! via the builder:
//!
//! ```rust
//! # use opentelemetry_sdk::logs::SdkLoggerProvider;
//! # use opentelemetry_stdout::LogExporter;
//! # let exporter = LogExporter::default();
//! # let provider = SdkLoggerProvider::builder().with_simple_exporter(exporter).build();
//! ```ignore
//! use opentelemetry_appender_tracing::layer::{OpenTelemetryTracingBridge, TracingSpanAttributes};
//!
//! // Copy ALL tracing-span attributes onto log records:
Expand All @@ -161,9 +159,10 @@
//!
//! ## Feature Flags
//!
//! - `experimental_span_attributes`: Enables tracing-span attribute enrichment
//! (`TracingSpanAttributes`, `with_tracing_span_attributes`).
//! - `experimental_metadata_attributes`: Adds source code metadata (`code.filepath`,
//! `code.filename`, `code.namespace`, `code.lineno`) as log record attributes.
//! This is experimental and may change in future releases.
//!
//!
//! ## Limitations
Expand Down
Loading